---
title: 接口隔离原则
description: Interface Segregation Principle (ISP) 
sidebar_position: 5
---

## 什么是接口隔离原则

接口隔离原则 (Interface Segregation Principle, ISP) 是 SOLID 原则之一，由 Robert Martin 提出。其核心思想是：

> Clients should not be forced to depend upon interfaces that they do not use.
> 
> 客户端不应该被强迫依赖它不需要的接口。

这里的"客户端"可以理解为接口的调用者或者使用者。

理解ISP的关键在于理解"接口"的含义，它可以有不同的解读。

## "接口"的三种理解与应用

### 1. 理解为一组 API 接口集合

当"接口"指代一组API集合时（如微服务接口、类库接口），ISP意味着：如果一个接口集合中的部分接口只被部分调用者使用，那么应该将这部分接口隔离出来，形成更小的接口集合，单独提供给需要的调用者。

**目的**：避免调用者依赖它们不需要的接口，减少耦合，提高安全性（如防止误用敏感接口）。

**示例**：

假设有一个`UserService`提供用户注册、登录、获取信息等通用功能。

```java
public interface UserService {
    boolean register(String cellphone, String password);
    boolean login(String cellphone, String password);
    UserInfo getUserInfoById(long id);
    UserInfo getUserInfoByCellphone(String cellphone);
}
```

现在需要一个删除用户的接口，但这个接口非常敏感，只希望给后台管理系统使用。

**不好的做法**：直接在`UserService`中添加`deleteUserById()`。
这会导致所有依赖`UserService`的系统都能看到并可能误用删除接口。

**符合ISP的做法**：创建一个新的、职责更单一的接口`RestrictedUserService`，专门放置敏感操作。

```java
public interface RestrictedUserService {
    boolean deleteUserByCellphone(String cellphone);
    boolean deleteUserById(long id);
}

// 实现类可以同时实现多个接口
public class UserServiceImpl implements UserService, RestrictedUserService {
    // ... 实现所有接口方法 ...
}
```

后台管理系统依赖 `RestrictedUserService`，其他系统仅依赖 `UserService`。

### 2. 理解为单个 API 接口或函数

当"接口"指代单个API或函数时，ISP意味着：函数的设计要功能单一，不要在一个函数中耦合多个不同的功能逻辑。如果调用者只需要函数的部分功能，那么这个函数可能职责不够单一，应该考虑拆分。

**目的**：提高函数的内聚性、复用性，避免调用者执行不必要的操作，提升性能。

**示例**：

一个统计函数`count()`计算并返回一个包含多种统计指标的对象。

```java
public class Statistics {
    private Long max;
    private Long min;
    private Long average;
    private Long sum;
    // ... 省略其他指标和方法 ...
}

public Statistics count(Collection<Long> dataSet) {
    Statistics statistics = new Statistics();
    // 计算所有指标：max, min, average, sum ...
    return statistics;
}
```

**问题**：如果某个调用者只需要`max`和`min`，但`count()`却计算了所有指标，造成了性能浪费。

**符合ISP的做法**：将`count()`拆分成多个功能更单一的函数。

```java
public Long max(Collection<Long> dataSet) { /* ... */ }
public Long min(Collection<Long> dataSet) { /* ... */ }
public Long average(Collection<Long> dataSet) { /* ... */ }
public Long sum(Collection<Long> dataSet) { /* ... */ }
```

调用者按需调用即可。

**注意**：判断职责是否单一需要结合场景。如果大部分调用者都需要所有统计信息，那么`count()`函数可能是合理的。

### 3. 理解为 OOP 中的接口概念

当"接口"指代面向对象编程语言中的`interface`时，ISP意味着：接口的设计要尽量小而专，包含尽可能少的方法。不要让实现类依赖它们不需要的方法，也不要强迫调用者依赖它们不需要的方法。

**目的**：提高接口的内聚性，降低实现类和调用者之间的耦合度，提高灵活性和可复用性。

**示例**：

假设有`RedisConfig`, `KafkaConfig`, `MysqlConfig`。需要支持部分配置的热更新(`update()`)和部分配置的信息查看(`output()`/`outputInPlainText()`)。

**不好的做法**：定义一个大而全的`Config`接口。

```java
public interface Config {
    void update();
    String outputInPlainText();
    Map<String, String> output();
}

// 所有Config类都需要实现这三个方法，即使它们不需要
public class KafkaConfig implements Config { /* update需要，output不需要但必须实现 */ }
public class MysqlConfig implements Config { /* update不需要但必须实现，output需要 */ }
```

**问题**：
1.  实现类被迫实现不必要的方法（如KafkaConfig的output）。
2.  调用者（如热更新调度器`ScheduledUpdater`）被迫依赖整个`Config`接口，即使它只关心`update()`。
3.  接口不灵活，添加新方法会影响所有实现类。

**符合ISP的做法**：定义两个小而专的接口`Updater`和`Viewer`。

```java
public interface Updater {
    void update();
}

public interface Viewer {
    String outputInPlainText();
    Map<String, String> output();
}

// 按需实现接口
public class RedisConfig implements Updater, Viewer { /* 实现所有 */ }
public class KafkaConfig implements Updater { /* 只实现 update */ }
public class MysqlConfig implements Viewer { /* 只实现 output */ }

// 调用者只依赖需要的接口
public class ScheduledUpdater {
    private Updater updater;
    public ScheduledUpdater(Updater updater, ...) { /* ... */ }
    public void run() { updater.update(); }
}

public class SimpleHttpServer {
    private Map<String, List<Viewer>> viewers = ...;
    public void addViewer(String url, Viewer viewer) { /* ... */ }
    public void run() { /* 调用 viewer.output... */ }
}
```

**好处**：
1.  实现类只实现需要的方法。
2.  调用者只依赖需要的接口，耦合度低。
3.  接口更灵活、可复用（例如，`Viewer`接口可以被其他需要展示信息的类如`Metrics`复用）。

## ISP 与 SRP 的区别

*   **单一职责原则 (SRP)**：主要关注模块、类、接口自身的职责是否单一。一个类或模块应该有且只有一个引起它变化的原因。
*   **接口隔离原则 (ISP)**：更侧重于接口的设计，并从 **调用者** 的角度来审视接口。它提供了一个判断接口职责是否单一的标准：如果调用者只使用了接口的部分方法或功能，那么该接口可能设计得不够单一，违反了ISP。

可以认为ISP是SRP在接口设计层面的一个具体体现和指导原则。

## 总结

接口隔离原则的核心是避免"胖接口"（Fat Interface），鼓励使用多个小而专的接口代替一个大而全的接口。遵循ISP可以带来以下好处：

*   **降低耦合**：调用者和实现类都只依赖于它们真正需要的部分。
*   **提高内聚**：接口功能更单一、更专注。
*   **提升可维护性**：修改一个接口的影响范围更小。
*   **增强灵活性和复用性**：小接口更容易被组合和复用。

在设计接口时，应始终考虑调用者的需求，保持接口的精简和专注。
